/*
   之前我们实现的栈和队列，底层是动态数组，是通过resize改变数组长度来实现动态，不算真正的动态，并且在内存中位置是连续的
   链表在内存中的位置可以不连续，通过指针指向下一个元素，也不用担心长度，把每个元素挂接起来实现真正的动态，但会失去随机访问功能。

    链表的初始化就像一个制造了一个火车头下一个是空。然后每个元素就是一个车厢

    链表中其实是没有索引概念的，这里利用索引只是为了好理解。


    时间复杂度   添加 O(n) 删除 O(n) 修改 O(n)  查找 O(n) 但是如果只对链表头操作就是O(1)时间复杂度
    链表比数组的时间复杂度高，但是链表可以实现真正的动态(不会大量的浪费内存空间)，且位置可以不连续
    如果只对链表头操作，链表与数组是一样效率的。

 */
public class LinkedList<E> {

    //LinkList的内部类,只有LinkedList可以访问，不用让客户知道底层是如何实现
    private class Node{
        public E e;
        public Node next;

        public Node(E e , Node next){
            this.e=e;
            this.next=next;
        }

        public Node(E e ){
           this(e,null);
        }

        public Node(){
            this(null,null);
        }

        public String toString(){
            return e.toString();
        }

    }

    //private Node head;  //维护每次插入的元素的位置。不是个节点
    private Node dummyhead; //虚拟头节点，本身就是个节点，为解决添加操作统一了逻辑，让方法使之复用
    private int size;   //链表的长度

    public LinkedList(){
        //head=null;  //不存在节点，不指向任何地方
        dummyhead =new Node(null,null); //一个null,null的节点
        size=0;
    }

    // 获取链表中的元素个数
    public int getSize(){
        return size;
    }

    // 返回链表是否为空
    public boolean isEmpty(){
        return size == 0;
    }

    // 在链表的index位置添加新的元素e
    // 在链表中不是一个常用的操作，练习用：）
    public void add(int index ,E e ){
        if(index<0 || index > size )
            throw new IllegalArgumentException("Add failed. Illegal index.");

       /* head的特殊情况，方法不能复用--为解决这种特殊使用虚拟头节点(dummyhead)
        if(index == 0)
            addFirst(e);

        //目的:找到索引是index的前一个的元素
        else{*/

            //因为它是第一次把null丢给进来的节点然后自己指向(变成)该节点，所以自己一直都是第一个元素
            //Node prev =head;  head指向链表的第一个元素

            //因为自己本身就是一个节点，进来的节点都是它的next,所以它一直都是第一个元素的前一个元素
        Node prev =dummyhead;    //dummyhead:指向链表的第一个元素的前一个元素

            //for(int i =0; i<index-1; i++)  //head的情况，每循环一次链表元素往后移一次，直到移动到index-1的位置
                //prev=prev.next;

        for(int i=0 ; i<index; i++) //dummyhead
            prev=prev.next;

        //插入
//            Node node =new Node(e);
//            node.next=prev.next;
//            prev.next=node;

        //上面三句等于这一句
        prev.next= new Node(e, prev.next) ;

        size++; //维护size

        }


    //向链表添加元素
    public void addFirst(E e){

//        Node node = new Node(e);
//        node.next=head;
//        head = node;

        //上面三句等于这一句
        //head = new Node(e, head);

        //上面是head的情况

        
        //使用dummyhead的话此时这里可以直接使用add复用
        add(0,e);

    }

    // 在链表末尾添加新的元素e
    public void addLast(E e){
        add(size, e);
    }


    // 得到链表第index个位置的元素
    // 在链表中不是一个常用的操作，练习用：）
    public E get(int index){

        if(index<0 || index >= size )
            throw new IllegalArgumentException("Get failed. Illegal index.");

        //dummyhead是第一个节点前面的节点，所以我们要从第一个节点开始遍历
        Node cur =dummyhead.next;

        for (int i=0; i<index; i++)
            cur = cur.next;

        return cur.e;

    }

    // 获得链表的第一个元素
    public E getFirst(){
        return get(0);
    }

    // 获得链表的最后一个元素
    public E getLast(){
        return get(size - 1);
    }

    // 修改链表的第index(0-based)个位置的元素为e
    // 在链表中不是一个常用的操作，练习用：）
    public void set(int index, E e){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Set failed. Illegal index.");

        Node cur = dummyhead.next; //从第一个元素开始

        for(int i = 0 ; i < index ; i ++)
            cur = cur.next;
        cur.e = e;

     }


    // 查找链表中是否有元素e
    public boolean contains(E e){

        Node cur =dummyhead.next;
        while(cur!=null){
            if(cur.e.equals(e))
                return true;
            cur=cur.next;
        }
        return false;
    }

    // 从链表中删除index(0-based)位置的元素, 返回删除的元素
    // 在链表中不是一个常用的操作，练习用：）
    public E remove(int index){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Remove failed. Index is illegal.");

        Node prev = dummyhead;

        for(int i =0 ; i<index; i++)
            //得到要移除的元素的前一个元素
            prev=prev.next;


        Node retNode = prev.next;
        prev.next=retNode.next;
        retNode.next=null; //让其进入垃圾回收范围
        size--;

        return retNode.e;

    }

    //移除第一个元素
    public E removeFirst(){
        return remove(0);
    }

    //移除最后一个元素
    public E removeLast(){
        return  remove(size-1);
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();

        Node cur =dummyhead.next;

        while(cur!=null){
            res.append(cur.next+"-->");
            cur=cur.next;
        }

        res.append("NULL");

        return  res.toString();
    }

}
